理解React ref及其使用
ref的作用,创建一个变量,变量中有一个current属性,将dom或者类组件的实例保存在ref的current属性上。虽然ref看起来是在类组件的props,或dom的属性上,但是实际上在React.createElement阶段,将ref从props中删除了,赋到了虚拟dom上。
- 将ref赋给dom的ref属性后,在虚拟dom创建完真实dom后,会将真实dom保存到ref.current,可以通过ref.current 直接操作dom上的方法或获取dom属性
- 将ref赋给类组件的ref属性后,在类组件实例化后,会将类组件的实例保存到ref.current,可以通过ref可以获取类组件实例上的属性,或调用类组件实例的方法
- 不能直接将ref赋给函数组件,因为函数组件本质上是一个函数,不像类组件一样有实例,也没有this指向,所以不能直接调用函数组件内部的方法或属性,必须将ref通过参数的显式传入函数组件,在函数组件内部将ref赋给dom元素。所以需要通过forwardRef转发ref。
创建ref
在类组件中创建ref
React.createRef
class Sum extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
}
在函数组件中使用hook创建ref
useRef
function Sum () {
const ref = React.useRef()
}
将ref赋值和使用ref
将ref赋给dom
直接将创建好的ref赋给dom
class Sum extends React.Component {
aRef = React.createRef()
bRef = React.createRef()
resultRef = React.createRef()
constructor(props) {
super(props)
}
handleAdd = () => {
let a = this.aRef.current.value;
let b = this.bRef.current.value;
this.resultRef.current.value = a + b;
}
render() {
return (
<>
<input ref={this.aRef} />+<input ref={this.bRef} /><button onClick={this.handleAdd}>=</button><input ref={this.resultRef} />
</>
);
}
}
将ref赋给类组件
如果将ref赋类组件,ref指向的是类组件的实例。 可以通过ref.current.xxx的方式,访问类组件实例内部的属性或方法,前提是类组件内部必须提供属性/方法供父组件调用。
class Child extends React.Component {
inputRef = React.createRef()
// 必须提供方法,供父组件ref调用
setFocus = () => {
this.inputRef.current.focus()
}
render() {
return <input ref={this.inputRef} />
}
}
class Parent extends React.Component {
childRef = React.createRef()
constructor(props) {
super(props)
}
setFocus = () => {
this.childRef.current.setFocus()
}
render() {
<div>
<Child ref={this.childRef}/>
<button onClick={this.setFocus}>设置焦点</button>
</div>
}
}
将ref赋给函数组件
使用forwordRef 将Child包裹,返回了一个forward_ref类型虚拟dom,。forward_ref类型虚拟dom中的type属性是一个对象,type内部包含两个属性,分别是 $$typeof
、render
{
$$typeof: react.forword_ref,
render: function FunctionComponent() {}
}
其中 $$typeof
标识这是一个forwardRef转发后的函数组件,render
存放着转发前的函数组件function方法。
在将forward_ref类型虚拟dom创建真实dom时,会调用 type.render
方法,并且将虚拟dom的props和ref传递过去,返回真实dom。所以需要被转发的函数组件,需要显式提供2个参数,分别是props和forwordRef。
forward_ref类型虚拟dom的type属性是一个对象,是一种特殊的类型。类组件、函数组件或dom的type属性,要么是字符串,要么是class或function。
function Child(props, forwordRef) {
return <input ref={forwordRef} />
}
// 使用 forwordRef 将需要设置ref的函数组件Child包裹,即为转发
const ForwordRefChild = React.forwordRef(Child)
class Parent extends React.Component {
childRef = React.createRef()
constructor(props) {
super(props)
}
setFocus = () => {
// 直接调用子组件赋给ref的dom上的方法
this.childRef.current.focus()
}
render() {
<div>
<ForwordRefChild ref={this.childRef}/>
<button onClick={this.setFocus}>设置焦点</button>
</div>
}
}